﻿using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.SceneManagement;
using YooAsset;

namespace UniFramework.Asset
{
	public class AssetLoader
	{
		protected List<AssetHandleWrap> _handleWrapList;
		private Dictionary<object, bool> _handleAsyncOperationDic;
		private ResourcePackage _defaultPackage;

		public void OnInit(ResourcePackage defaultPackage)
		{
			_defaultPackage = defaultPackage;
			_handleWrapList = new List<AssetHandleWrap>();
			_handleAsyncOperationDic = new Dictionary<object, bool>();
		}

		public async void OnDispose()
		{
			foreach (var assetHandleWrap in _handleWrapList) assetHandleWrap?.Release();
			_handleWrapList.Clear();
			_handleAsyncOperationDic.Clear();
			await _defaultPackage.UnloadUnusedAssetsAsync();
		}

		/// <summary>
		/// 同步加载子资源对象
		/// </summary>
		/// <param name="assetName"></param>
		/// <param name="singleAssetName"></param>
		/// <param name="loadFinishCallBack"></param>
		/// <typeparam name="T"></typeparam>
		/// <returns></returns>
		public T LoadSubAssetsSync<T>(string assetName, string singleAssetName,
			Action<bool, T> loadFinishCallBack = null)
			where T : UnityEngine.Object
		{
			if (_defaultPackage.CheckLocationValid(assetName))
			{
				var subAssetsHandle = _defaultPackage.LoadSubAssetsSync<T>(assetName);
				if (subAssetsHandle.Status == EOperationStatus.Succeed)
				{
					foreach (var asset in subAssetsHandle.SubAssetObjects)
					{
						if (asset.name == singleAssetName)
						{
							AssetHandleWrap assetHandleWrap = new AssetHandleWrap(subAssetsHandle);
							_handleWrapList.Add(assetHandleWrap);
							T t = asset as T;
							loadFinishCallBack?.Invoke(true, t);
							return t;
						}
					}
				}
			}

			loadFinishCallBack?.Invoke(false, null);
			return null;
		}

		/// <summary>
		/// 同步加载资源
		/// </summary>
		/// <param name="assetName"></param>
		/// <param name="loadFinishCallBack"></param>
		/// <typeparam name="T"></typeparam>
		/// <returns></returns>
		public T LoadAssetSync<T>(string assetName, Action<bool, T> loadFinishCallBack = null)
			where T : UnityEngine.Object
		{
			if (_defaultPackage.CheckLocationValid(assetName))
			{
				AssetHandle assetHandle = _defaultPackage.LoadAssetSync<T>(assetName);
				if (assetHandle.Status == EOperationStatus.Succeed)
				{
					AssetHandleWrap assetHandleWrap = new AssetHandleWrap(assetHandle);
					_handleWrapList.Add(assetHandleWrap);
					T prefab = assetHandle.AssetObject as T;
					loadFinishCallBack?.Invoke(true, prefab);
					return prefab;
				}
			}

			loadFinishCallBack?.Invoke(false, null);
			return null;
		}

		/// <summary>
		/// 注册异步句柄
		/// </summary>
		/// <param name="handle"></param>
		/// <returns></returns>
		public bool TryRegisterHandle(object handle)
		{
			if (handle == null) return false;
			return _handleAsyncOperationDic.TryAdd(handle, true);
		}

		/// <summary>
		/// 取消异步句柄
		/// </summary>
		/// <param name="handle"></param>
		/// <returns></returns>
		public bool TryUnRegisterHandle(object handle)
		{
			if (handle == null) return false;
			if (_handleAsyncOperationDic.ContainsKey(handle))
			{
				_handleAsyncOperationDic.Remove(handle);
				return true;
			}

			return false;
		}

		/// <summary>
		/// 异步句柄是否合法
		/// </summary>
		/// <param name="handle"></param>
		/// <returns></returns>
		private bool AsyncOperationIsLegal(object handle)
		{
			if (handle == null) return true;
			return _handleAsyncOperationDic.ContainsKey(handle);
		}

		/// <summary>
		/// 异步加载子资源对象
		/// 异步安全
		/// </summary>
		/// <param name="assetName"></param>
		/// <param name="singleAssetName"></param>
		/// <param name="loadFinishCallBack"></param>
		/// <param name="handler"></param>
		/// <returns></returns>
		public async UniTask<T> LoadSubAssetsAsync<T>(string assetName, string singleAssetName,
			Action<bool, T> loadFinishCallBack,
			object handler) where T : UnityEngine.Object
		{
			if (_defaultPackage.CheckLocationValid(assetName))
			{
				TryRegisterHandle(handler);
				var subAssetsHandle = _defaultPackage.LoadSubAssetsAsync(assetName);
				await subAssetsHandle;
				if (subAssetsHandle.Status == EOperationStatus.Succeed)
				{
					foreach (var asset in subAssetsHandle.SubAssetObjects)
					{
						if (asset.name == singleAssetName)
						{
							AssetHandleWrap assetHandleWrap = new AssetHandleWrap(subAssetsHandle);
							_handleWrapList.Add(assetHandleWrap);
							T t = asset as T;
							if (AsyncOperationIsLegal(handler))
								loadFinishCallBack?.Invoke(true, t);
							TryUnRegisterHandle(handler);
							return t;
						}
					}
				}
			}

			if (AsyncOperationIsLegal(handler))
				loadFinishCallBack?.Invoke(false, null);
			TryUnRegisterHandle(handler);
			return null;
		}

		/// <summary>
		/// 异步加载资源
		/// 异步安全
		/// </summary>
		/// <param name="assetName"></param>
		/// <param name="loadFinishCallBack"></param>
		/// <param name="handler"></param>
		/// <typeparam name="T"></typeparam>
		/// <returns></returns>
		public async UniTask<T> LoadAssetsAsync<T>(string assetName, Action<bool, T> loadFinishCallBack,
			object handler = null) where T : UnityEngine.Object
		{
			if (_defaultPackage.CheckLocationValid(assetName))
			{
				TryRegisterHandle(handler);
				var assetHandle = _defaultPackage.LoadAssetAsync<T>(assetName);
				await assetHandle;
				if (assetHandle.Status == EOperationStatus.Succeed)
				{
					AssetHandleWrap assetHandleWrap = new AssetHandleWrap(assetHandle);
					_handleWrapList.Add(assetHandleWrap);
					T t = assetHandle.AssetObject as T;
					if (AsyncOperationIsLegal(handler))
						loadFinishCallBack?.Invoke(true, t);
					TryUnRegisterHandle(handler);
					return t;
				}
			}

			if (AsyncOperationIsLegal(handler))
				loadFinishCallBack?.Invoke(false, null);
			TryUnRegisterHandle(handler);
			return null;
		}

		/// <summary>
		/// 异步加载场景
		/// 异步不安全
		/// </summary>
		/// <param name="sceneName"></param>
		/// <param name="finishCallBack"></param>
		/// <returns></returns>
		/// <exception cref="Exception"></exception>
		public async UniTask<SceneHandle> LoadSceneAsync(string sceneName, Action<bool> finishCallBack = null)
		{
			var activeScene = SceneManager.GetActiveScene();
			if (activeScene.name == sceneName) throw new Exception("scene already loaded");
			var sceneMode = LoadSceneMode.Single;
			var physicsMode = LocalPhysicsMode.None;
			SceneHandle handle = _defaultPackage.LoadSceneAsync(sceneName, sceneMode, physicsMode);
			await handle;
			if (handle.Status == EOperationStatus.Succeed)
			{
				AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName);
				if (asyncLoad == null) throw new Exception("asyncLoad is null");
				while (!asyncLoad.isDone) await UniTask.Yield();
				finishCallBack?.Invoke(true);
				return handle;
			}

			finishCallBack?.Invoke(false);
			return null;
		}
	}
}